package evl

import (
	"testing"

	"gitcode.com/deyiyangyang/bampoo/ast"
	"gitcode.com/deyiyangyang/bampoo/obj"
)

// TestEvalComparisonGreater tests the (>) greater than comparison
func TestEvalComparisonGreater(t *testing.T) {
	env := obj.NewEnvironment()
	evaluator := New(env)

	tests := []struct {
		name     string
		expr     *ast.ComparisonExpr
		expected bool
	}{{
		name: "greater than with 5 > 3",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
			},
		},
		expected: true,
	}, {
		name: "greater than with 3 > 5",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: false,
	}, {
		name: "greater than with 5 > 5",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: false,
	}, {
		name: "greater than with negative numbers -3 > -5",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: -3},
				&ast.IntegerLiteral{Value: -5},
			},
		},
		expected: true,
	}, {
		name: "greater than with chained comparison 5 > 3 > 1",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 1},
			},
		},
		expected: true,
	}, {
		name: "greater than with failing chained comparison 5 > 3 > 4",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 4},
			},
		},
		expected: false,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := evaluator.Eval(tt.expr)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			boolVal, ok := result.(*obj.BooleanValue)
			if !ok {
				t.Fatalf("expected BooleanValue, got %T", result)
			}

			if boolVal.Value != tt.expected {
				t.Errorf("expected %v, got %v", tt.expected, boolVal.Value)
			}
		})
	}
}

// TestEvalComparisonLess tests the (<) less than comparison
func TestEvalComparisonLess(t *testing.T) {
	env := obj.NewEnvironment()
	evaluator := New(env)

	tests := []struct {
		name     string
		expr     *ast.ComparisonExpr
		expected bool
	}{{
		name: "less than with 3 < 5",
		expr: &ast.ComparisonExpr{
			Operator: "<",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: true,
	}, {
		name: "less than with 5 < 3",
		expr: &ast.ComparisonExpr{
			Operator: "<",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
			},
		},
		expected: false,
	}, {
		name: "less than with 5 < 5",
		expr: &ast.ComparisonExpr{
			Operator: "<",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: false,
	}, {
		name: "less than with negative numbers -5 < -3",
		expr: &ast.ComparisonExpr{
			Operator: "<",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: -5},
				&ast.IntegerLiteral{Value: -3},
			},
		},
		expected: true,
	}, {
		name: "less than with chained comparison 1 < 3 < 5",
		expr: &ast.ComparisonExpr{
			Operator: "<",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 1},
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: true,
	}, {
		name: "less than with failing chained comparison 1 < 3 < 2",
		expr: &ast.ComparisonExpr{
			Operator: "<",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 1},
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 2},
			},
		},
		expected: false,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := evaluator.Eval(tt.expr)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			boolVal, ok := result.(*obj.BooleanValue)
			if !ok {
				t.Fatalf("expected BooleanValue, got %T", result)
			}

			if boolVal.Value != tt.expected {
				t.Errorf("expected %v, got %v", tt.expected, boolVal.Value)
			}
		})
	}
}

// TestEvalComparisonEqual tests the (=) equality comparison
func TestEvalComparisonEqual(t *testing.T) {
	env := obj.NewEnvironment()
	evaluator := New(env)

	tests := []struct {
		name     string
		expr     *ast.ComparisonExpr
		expected bool
	}{{
		name: "equality with 5 = 5",
		expr: &ast.ComparisonExpr{
			Operator: "=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: true,
	}, {
		name: "equality with 5 = 3",
		expr: &ast.ComparisonExpr{
			Operator: "=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
			},
		},
		expected: false,
	}, {
		name: "equality with negative numbers -5 = -5",
		expr: &ast.ComparisonExpr{
			Operator: "=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: -5},
				&ast.IntegerLiteral{Value: -5},
			},
		},
		expected: true,
	}, {
		name: "equality with chained comparison 5 = 5 = 5",
		expr: &ast.ComparisonExpr{
			Operator: "=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: true,
	}, {
		name: "equality with failing chained comparison 5 = 5 = 3",
		expr: &ast.ComparisonExpr{
			Operator: "=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
			},
		},
		expected: false,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := evaluator.Eval(tt.expr)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			boolVal, ok := result.(*obj.BooleanValue)
			if !ok {
				t.Fatalf("expected BooleanValue, got %T", result)
			}

			if boolVal.Value != tt.expected {
				t.Errorf("expected %v, got %v", tt.expected, boolVal.Value)
			}
		})
	}
}

// TestEvalComparisonGreaterOrEqual tests the (>=) greater than or equal comparison
func TestEvalComparisonGreaterOrEqual(t *testing.T) {
	env := obj.NewEnvironment()
	evaluator := New(env)

	tests := []struct {
		name     string
		expr     *ast.ComparisonExpr
		expected bool
	}{{
		name: "greater or equal with 5 >= 3",
		expr: &ast.ComparisonExpr{
			Operator: ">=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
			},
		},
		expected: true,
	}, {
		name: "greater or equal with 5 >= 5",
		expr: &ast.ComparisonExpr{
			Operator: ">=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: true,
	}, {
		name: "greater or equal with 3 >= 5",
		expr: &ast.ComparisonExpr{
			Operator: ">=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: false,
	}, {
		name: "greater or equal with negative numbers -3 >= -5",
		expr: &ast.ComparisonExpr{
			Operator: ">=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: -3},
				&ast.IntegerLiteral{Value: -5},
			},
		},
		expected: true,
	}, {
		name: "greater or equal with -3 >= -3",
		expr: &ast.ComparisonExpr{
			Operator: ">=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: -3},
				&ast.IntegerLiteral{Value: -3},
			},
		},
		expected: true,
	}, {
		name: "greater or equal with chained comparison 5 >= 3 >= 1",
		expr: &ast.ComparisonExpr{
			Operator: ">=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 1},
			},
		},
		expected: true,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := evaluator.Eval(tt.expr)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			boolVal, ok := result.(*obj.BooleanValue)
			if !ok {
				t.Fatalf("expected BooleanValue, got %T", result)
			}

			if boolVal.Value != tt.expected {
				t.Errorf("expected %v, got %v", tt.expected, boolVal.Value)
			}
		})
	}
}

// TestEvalComparisonLessOrEqual tests the (<=) less than or equal comparison
func TestEvalComparisonLessOrEqual(t *testing.T) {
	env := obj.NewEnvironment()
	evaluator := New(env)

	tests := []struct {
		name     string
		expr     *ast.ComparisonExpr
		expected bool
	}{{
		name: "less or equal with 3 <= 5",
		expr: &ast.ComparisonExpr{
			Operator: "<=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: true,
	}, {
		name: "less or equal with 5 <= 5",
		expr: &ast.ComparisonExpr{
			Operator: "<=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: true,
	}, {
		name: "less or equal with 5 <= 3",
		expr: &ast.ComparisonExpr{
			Operator: "<=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
			},
		},
		expected: false,
	}, {
		name: "less or equal with negative numbers -5 <= -3",
		expr: &ast.ComparisonExpr{
			Operator: "<=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: -5},
				&ast.IntegerLiteral{Value: -3},
			},
		},
		expected: true,
	}, {
		name: "less or equal with -5 <= -5",
		expr: &ast.ComparisonExpr{
			Operator: "<=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: -5},
				&ast.IntegerLiteral{Value: -5},
			},
		},
		expected: true,
	}, {
		name: "less or equal with chained comparison 1 <= 3 <= 5",
		expr: &ast.ComparisonExpr{
			Operator: "<=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 1},
				&ast.IntegerLiteral{Value: 3},
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expected: true,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := evaluator.Eval(tt.expr)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			boolVal, ok := result.(*obj.BooleanValue)
			if !ok {
				t.Fatalf("expected BooleanValue, got %T", result)
			}

			if boolVal.Value != tt.expected {
				t.Errorf("expected %v, got %v", tt.expected, boolVal.Value)
			}
		})
	}
}

// TestEvalComparisonErrorCases tests error cases for comparison expressions
func TestEvalComparisonErrorCases(t *testing.T) {
	env := obj.NewEnvironment()
	evaluator := New(env)

	tests := []struct {
		name        string
		expr        *ast.ComparisonExpr
		expectedErr string
	}{{
		name: "too few operands",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
			},
		},
		expectedErr: "requires at least 2 operands",
	}, {
		name: "non-numeric comparison with >",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.StringLiteral{Value: "hello"},
				&ast.StringLiteral{Value: "world"},
			},
		},
		expectedErr: "cannot perform comparison '>' on non-numeric values",
	}, {
		name: "non-numeric comparison with <",
		expr: &ast.ComparisonExpr{
			Operator: "<",
			Operands: []ast.Expression{
				&ast.StringLiteral{Value: "hello"},
				&ast.StringLiteral{Value: "world"},
			},
		},
		expectedErr: "cannot perform comparison '<' on non-numeric values",
	}, {
		name: "non-numeric comparison with >=",
		expr: &ast.ComparisonExpr{
			Operator: ">=",
			Operands: []ast.Expression{
				&ast.StringLiteral{Value: "hello"},
				&ast.StringLiteral{Value: "world"},
			},
		},
		expectedErr: "cannot perform comparison '>=' on non-numeric values",
	}, {
		name: "non-numeric comparison with <=",
		expr: &ast.ComparisonExpr{
			Operator: "<=",
			Operands: []ast.Expression{
				&ast.StringLiteral{Value: "hello"},
				&ast.StringLiteral{Value: "world"},
			},
		},
		expectedErr: "cannot perform comparison '<=' on non-numeric values",
	}, {
		name: "mixed numeric and non-numeric comparison with >",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.StringLiteral{Value: "hello"},
			},
		},
		expectedErr: "cannot perform comparison '>' on non-numeric values",
	}, {
		name: "unknown operator",
		expr: &ast.ComparisonExpr{
			Operator: "!=",
			Operands: []ast.Expression{
				&ast.IntegerLiteral{Value: 5},
				&ast.IntegerLiteral{Value: 3},
			},
		},
		expectedErr: "unknown comparison operator",
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			_, err := evaluator.Eval(tt.expr)
			if err == nil {
				t.Fatal("expected error but got nil")
			}

			if !containsErrorMessage(err.Error(), tt.expectedErr) {
				t.Errorf("expected error message to contain '%s', got '%s'", tt.expectedErr, err.Error())
			}
		})
	}
}

// TestEvalComparisonWithVariables tests comparison expressions with variables
func TestEvalComparisonWithVariables(t *testing.T) {
	env := obj.NewEnvironment()
	evaluator := New(env)

	// Define some variables
	env.Set("x", &obj.IntegerValue{Value: 5})
	env.Set("y", &obj.IntegerValue{Value: 3})
	env.Set("z", &obj.IntegerValue{Value: 5})
	env.Set("str1", &obj.StringValue{Value: "hello"})
	env.Set("str2", &obj.StringValue{Value: "hello"})
	env.Set("str3", &obj.StringValue{Value: "world"})

	tests := []struct {
		name     string
		expr     *ast.ComparisonExpr
		expected bool
	}{{
		name: "variable comparison x > y",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.Symbol{Value: "x"},
				&ast.Symbol{Value: "y"},
			},
		},
		expected: true,
	}, {
		name: "variable comparison x = z",
		expr: &ast.ComparisonExpr{
			Operator: "=",
			Operands: []ast.Expression{
				&ast.Symbol{Value: "x"},
				&ast.Symbol{Value: "z"},
			},
		},
		expected: true,
	}, {
		name: "variable comparison y <= x",
		expr: &ast.ComparisonExpr{
			Operator: "<=",
			Operands: []ast.Expression{
				&ast.Symbol{Value: "y"},
				&ast.Symbol{Value: "x"},
			},
		},
		expected: true,
	}, {
		name: "mixed variable and literal comparison x > 4",
		expr: &ast.ComparisonExpr{
			Operator: ">",
			Operands: []ast.Expression{
				&ast.Symbol{Value: "x"},
				&ast.IntegerLiteral{Value: 4},
			},
		},
		expected: true,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := evaluator.Eval(tt.expr)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			boolVal, ok := result.(*obj.BooleanValue)
			if !ok {
				t.Fatalf("expected BooleanValue, got %T", result)
			}

			if boolVal.Value != tt.expected {
				t.Errorf("expected %v, got %v", tt.expected, boolVal.Value)
			}
		})
	}
}

// TestEvalComplexComparisonExpressions tests complex nested comparison expressions
func TestEvalComplexComparisonExpressions(t *testing.T) {
	env := obj.NewEnvironment()
	evaluator := New(env)

	tests := []struct {
		name     string
		expr     ast.Expression
		expected bool
	}{{
		name: "and with comparison expressions",
		expr: &ast.LogicalExpr{
			Operator: "and",
			Operands: []ast.Expression{
				&ast.ComparisonExpr{
					Operator: ">",
					Operands: []ast.Expression{
						&ast.IntegerLiteral{Value: 5},
						&ast.IntegerLiteral{Value: 3},
					},
				},
				&ast.ComparisonExpr{
					Operator: "<",
					Operands: []ast.Expression{
						&ast.IntegerLiteral{Value: 3},
						&ast.IntegerLiteral{Value: 10},
					},
				},
			},
		},
		expected: true,
	}, {
		name: "or with comparison expressions",
		expr: &ast.LogicalExpr{
			Operator: "or",
			Operands: []ast.Expression{
				&ast.ComparisonExpr{
					Operator: ">",
					Operands: []ast.Expression{
						&ast.IntegerLiteral{Value: 3},
						&ast.IntegerLiteral{Value: 5},
					},
				},
				&ast.ComparisonExpr{
					Operator: "<",
					Operands: []ast.Expression{
						&ast.IntegerLiteral{Value: 3},
						&ast.IntegerLiteral{Value: 10},
					},
				},
			},
		},
		expected: true,
	}, {
		name: "not with comparison expression",
		expr: &ast.LogicalExpr{
			Operator: "not",
			Operands: []ast.Expression{
				&ast.ComparisonExpr{
					Operator: "=",
					Operands: []ast.Expression{
						&ast.IntegerLiteral{Value: 5},
						&ast.IntegerLiteral{Value: 3},
					},
				},
			},
		},
		expected: true,
	}, {
		name: "if with comparison condition",
		expr: &ast.IfExpr{
			Condition: &ast.ComparisonExpr{
				Operator: ">",
				Operands: []ast.Expression{
					&ast.IntegerLiteral{Value: 5},
					&ast.IntegerLiteral{Value: 3},
				},
			},
			Consequence: &ast.BooleanLiteral{Value: true},
			Alternative: &ast.BooleanLiteral{Value: false},
		},
		expected: true,
	}}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result, err := evaluator.Eval(tt.expr)
			if err != nil {
				t.Fatalf("unexpected error: %v", err)
			}

			boolVal, ok := result.(*obj.BooleanValue)
			if !ok {
				t.Fatalf("expected BooleanValue, got %T", result)
			}

			if boolVal.Value != tt.expected {
				t.Errorf("expected %v, got %v", tt.expected, boolVal.Value)
			}
		})
	}
}

// containsErrorMessage checks if an error message contains a specific substring
func containsErrorMessage(s, substr string) bool {
	return len(s) >= len(substr) && s[len(s)-len(substr):] == substr
}
